//Download by http://www.NewXing.com

#include <stdio.h>
#include <Windows.h>
#include <mmsystem.h>
#include <d3dx9.h>
#include <math.h>

#include "SkinMesh.h"
#include "Map.h"


const float fWalkStep=10.0f;
const float fZoomStep=50.0f;

HWND						hWnd;
HFONT						hFont			=NULL;

LPDIRECT3D9					g_pD3D			=NULL;
LPDIRECT3DDEVICE9			g_pd3dDevice	=NULL;
LPD3DXFONT					g_pD3DFont		=NULL;

D3DXVECTOR3					vEyePt( 1800.0f, 2545.0f , -1800.0f);

CSkinMesh*					g_pRole			=NULL;
CMap*						g_pMap			=NULL;

HRESULT InitD3D()
{
	if(NULL==(g_pD3D=Direct3DCreate9(D3D_SDK_VERSION)))
		return E_FAIL;
	
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp,sizeof(d3dpp));
	d3dpp.Windowed=TRUE;
	d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat=D3DFMT_UNKNOWN;
	d3dpp.EnableAutoDepthStencil=TRUE;
	d3dpp.AutoDepthStencilFormat=D3DFMT_D16;
	
	if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,&g_pd3dDevice)))
	{
		return E_FAIL;
	}
	
	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING,         FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_DITHERENABLE,     TRUE );
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE,          TRUE );
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE,         D3DCULL_CCW );
    g_pd3dDevice->SetRenderState( D3DRS_AMBIENT,          0x33333333 );
    g_pd3dDevice->SetRenderState( D3DRS_NORMALIZENORMALS, TRUE );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	
	//init D3DFont
	hFont=CreateFont( 0, 0, 0, 0, 0, 0,
		FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
		VARIABLE_PITCH, "Arial" );
	if(FAILED(D3DXCreateFont(g_pd3dDevice,hFont,&g_pD3DFont)))
	{
		return E_FAIL;
	}
	
	return S_OK;
}


VOID Cleanup()
{
	if(g_pD3DFont!=NULL)
		g_pD3DFont->Release();	
	DeleteObject(hFont);
	
	if(g_pd3dDevice!=NULL)
		g_pd3dDevice->Release();
	
	if(g_pD3D!=NULL)
		g_pD3D->Release();
}

void Zoom(float fStep)
{
	vEyePt.x+=fStep;
	vEyePt.z-=fStep;
	vEyePt.y=(float)sqrt(vEyePt.x*vEyePt.x+vEyePt.z*vEyePt.z);
}



void SetupView()
{
	D3DXMATRIXA16 matView;
	D3DXVECTOR3	vLookatPt( -200.0f, 0.0f, 200.0f );
	D3DXVECTOR3	vUpVec( 0.0f, 1.0f, 0.0f );
	D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
	g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
}

void SetupProjection()
{
	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 100.0f, 100000.0f );
	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

void SetupWorld()
{
	D3DXMATRIXA16 matWorld;
	D3DXMatrixIdentity(&matWorld);
	g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
}

VOID Render()
{
	g_pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(160,160,160),1.0f,0);
	
	D3DXVECTOR3	vMousePt;
	if(SUCCEEDED(g_pd3dDevice->BeginScene()))
	{
		SetupWorld();
		SetupView();
		
		if(GetCapture())
		{
			D3DXMATRIXA16 matProj,matView,matWorld;
			g_pd3dDevice->GetTransform( D3DTS_PROJECTION, &matProj );
			g_pd3dDevice->GetTransform( D3DTS_VIEW, &matView );
			g_pd3dDevice->GetTransform( D3DTS_WORLD, &matWorld );
			
			D3DVIEWPORT9 viewPort;
			g_pd3dDevice->GetViewport(&viewPort);
			
			POINT ptCursor;
			GetCursorPos( &ptCursor );
			ScreenToClient( hWnd, &ptCursor );
			
			D3DXVECTOR3 vScreen((float)ptCursor.x,(float)ptCursor.y,0.0f),vOut;
			
			D3DXVec3Unproject(&vOut,&vScreen,&viewPort,&matProj,&matView,&matWorld);
			
			D3DXPLANE plane;
			D3DXVECTOR3 v1(1.0f,0.0f,1.0f);
			D3DXVECTOR3 v2(-1.0f,0.0f,1.0f);
			D3DXVECTOR3 v3(-1.0f,0.0f,-1.0f);
			D3DXPlaneFromPoints( &plane, &v1, &v2, &v3 );
			D3DXPlaneIntersectLine( &vMousePt, &plane, &vEyePt, &vOut );
			
			float fAngle1=(float)atan2(vMousePt.z,vMousePt.x);
			float fAngle2=3*D3DX_PI/2-fAngle1;
			g_pRole->SetRotateAngle(fAngle2);
			g_pMap->CenterTranslation(fWalkStep*((float)cos(fAngle1)),fWalkStep*((float)sin(fAngle1)));
		}		
		g_pMap->Render();
		g_pRole->Render();
		g_pd3dDevice->EndScene();
	}
	char textBuffer[256];
	sprintf(textBuffer,"mouse drag:    move\npagedown  :     zoom in \npageup       :     zoom out");
	g_pD3DFont->Begin();
	RECT rText;
	rText.top=10; rText.right=10; rText.left=400; rText.bottom=100;
	g_pD3DFont->DrawText(textBuffer,-1,&rText,DT_LEFT,D3DCOLOR_XRGB(255,255,255));
	g_pD3DFont->End();	
	
	g_pd3dDevice->Present(NULL,NULL,NULL,NULL);
}

LRESULT WINAPI MsgProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
	switch(msg)
	{
	case WM_LBUTTONDOWN:
		SetCapture( hWnd );
		g_pRole->SetAnim(TRUE);
		return 0;
	case WM_LBUTTONUP:
		ReleaseCapture();
		g_pRole->SetAnim(FALSE);
		return 0;
	case WM_KEYDOWN:
		switch(wParam)
		{
		case VK_NEXT:
			Zoom(-fZoomStep);;
			break;
		case VK_PRIOR:
			Zoom(fZoomStep);
			break;
		}
		return 0;
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage(0);
		return 0;
	}
	
	return DefWindowProc(hWnd,msg,wParam,lParam);
}

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,INT)
{
	WNDCLASSEX wc={sizeof(WNDCLASSEX),CS_CLASSDC,MsgProc,0L,0L,
		GetModuleHandle(NULL),NULL, NULL, NULL, NULL,
		"My D3D",NULL};
	RegisterClassEx(&wc);
	
	hWnd=CreateWindow("My D3D","http://kuga.51.net",
		WS_OVERLAPPEDWINDOW,100,60,600,480,
		GetDesktopWindow(),NULL,wc.hInstance,NULL);
	
	if(SUCCEEDED(InitD3D()))
	{
		SetupView();
		SetupProjection();
		
		g_pRole=new CSkinMesh(g_pd3dDevice);
		g_pRole->LoadFromXFile("tiny.x");
		g_pRole->SetAnim(FALSE);
		
		g_pMap=new CMap(g_pd3dDevice,10000,10000);
		g_pMap->InitMap("maze.bmp");
		
		ShowWindow(hWnd,SW_SHOWDEFAULT);
		UpdateWindow(hWnd);
		
		MSG msg;
		ZeroMemory(&msg,sizeof(msg));
		
		while(msg.message!=WM_QUIT)
		{
			if(PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else Render();
		}
		
		delete g_pMap;
		delete g_pRole;
	}
	
	UnregisterClass("My D3D",wc.hInstance);
	return 0;
}